package restserver

import (
	"context"
	"encoding/json"
	"errors"
	"gitee.com/vrv_media/go-micro-framework/pkg/httputil"
	"gitee.com/vrv_media/go-micro-framework/registry"
	"gitee.com/vrv_media/go-micro-framework/registry/balance/round_robin"
	"gitee.com/vrv_media/go-micro-framework/registry/discovery/native_config"
	"gitee.com/vrv_media/go-micro-framework/registry/resolver"
	"github.com/go-resty/resty/v2"
)

type ClientOption func(c *Client)

type Client struct {
	// 服务信息
	serverName string
	endpoint   string
	// 获取client的方法，可以定制获取的方法
	restyClientFunc func() *resty.Client
	// discovery 接口
	discovery registry.Discovery // 服务发现
	resolver  registry.Resolver  // 服务解析
	balance   registry.Balancer  // 负载均衡
}

func NewDefaultClient(serverName string, endpoint string, opts ...ClientOption) *Client {
	client := &Client{
		serverName:      serverName,
		endpoint:        endpoint,
		restyClientFunc: httputil.GetDefaultClient,
	}
	if len(opts) > 0 {
		for _, o := range opts {
			if o == nil {
				continue
			}
			o(client)
		}
	}
	if client.restyClientFunc == nil {
		client.restyClientFunc = httputil.GetDefaultClient
	}
	// 补充服务发现等信息
	if client.discovery == nil {
		client.discovery = native_config.NewNativeConfigDiscovery(endpoint)
	}
	if client.balance == nil {
		client.balance = round_robin.NewRoundRobinBalancer()
	}
	if client.resolver == nil {
		client.resolver = resolver.NewResolve(serverName, client.discovery, client.balance)
	}
	return client
}

func (c *Client) NextAddress(ctx context.Context) (string, error) {
	// 从服务发现中获取到下一个地址
	selected, err := c.resolver.Next(ctx)
	if err != nil {
		return "", err
	}
	return selected.Address(), nil
}

func (c *Client) Get(ctx context.Context, path string) (int, []byte, error) {
	// 首先获取到地址
	address, err := c.NextAddress(ctx)
	if err != nil {
		return 0, nil, err
	}
	if address == "" {
		return 0, nil, errors.New("get address is empty")
	}
	url := address
	if path != "" {
		url += path
	}
	if c.restyClientFunc == nil {
		c.restyClientFunc = httputil.GetDefaultClient
	}
	client := c.restyClientFunc()
	return httputil.GetWithClient(client, url)
}

func (c *Client) GetWithResult(ctx context.Context, path string, result interface{}) (int, error) {
	statusCode, bytes, err := c.Get(ctx, path)
	if err != nil {
		return statusCode, err
	}
	if len(bytes) > 0 || result != nil {
		err = json.Unmarshal(bytes, result)
		if err != nil {
			return statusCode, err
		}
	}
	return statusCode, nil
}

func (c *Client) Post(ctx context.Context, path string, body interface{}) (int, []byte, error) {
	// 首先获取到下一个host
	address, err := c.NextAddress(ctx)
	if err != nil {
		return 0, nil, err
	}
	if address == "" {
		return 0, nil, errors.New("get address is empty")
	}
	url := address
	if path != "" {
		url += path
	}
	if c.restyClientFunc == nil {
		c.restyClientFunc = httputil.GetDefaultClient
	}
	var requestData []byte
	if body != nil {
		bytes, err := json.Marshal(body)
		if err != nil {
			return 0, []byte{}, err
		}
		requestData = bytes
	}
	client := c.restyClientFunc()
	return httputil.PostWithClient(client, url, requestData)
}

func (c *Client) PostWithResult(ctx context.Context, path string, body interface{}, result interface{}) (int, error) {
	statusCode, bytes, err := c.Post(ctx, path, body)
	if err != nil {
		return statusCode, err
	}
	if len(bytes) > 0 || result != nil {
		err = json.Unmarshal(bytes, result)
		if err != nil {
			return statusCode, err
		}
	}
	return statusCode, nil
}

func (c *Client) Put(ctx context.Context, path string, body interface{}) (int, []byte, error) {
	// 首先获取到下一个host
	address, err := c.NextAddress(ctx)
	if err != nil {
		return 0, nil, err
	}
	if address == "" {
		return 0, nil, errors.New("get address is empty")
	}
	url := address
	if path != "" {
		url += path
	}
	if c.restyClientFunc == nil {
		c.restyClientFunc = httputil.GetDefaultClient
	}
	var requestData []byte
	if body != nil {
		bytes, err := json.Marshal(body)
		if err != nil {
			return 0, []byte{}, err
		}
		requestData = bytes
	}
	client := c.restyClientFunc()
	return httputil.PutWithClient(client, url, requestData)
}

func (c *Client) PutWithResult(ctx context.Context, path string, body interface{}, result interface{}) (int, error) {
	statusCode, bytes, err := c.Put(ctx, path, body)
	if err != nil {
		return statusCode, err
	}
	if len(bytes) > 0 || result != nil {
		err = json.Unmarshal(bytes, result)
		if err != nil {
			return statusCode, err
		}
	}
	return statusCode, nil
}
func (c *Client) Delete(ctx context.Context, path string, body interface{}) (int, []byte, error) {
	// 首先获取到下一个host
	address, err := c.NextAddress(ctx)
	if err != nil {
		return 0, nil, err
	}
	if address == "" {
		return 0, nil, errors.New("get address is empty")
	}
	url := address
	if path != "" {
		url += path
	}
	if c.restyClientFunc == nil {
		c.restyClientFunc = httputil.GetDefaultClient
	}
	var requestData []byte
	if body != nil {
		bytes, err := json.Marshal(body)
		if err != nil {
			return 0, []byte{}, err
		}
		requestData = bytes
	}
	client := c.restyClientFunc()
	return httputil.DeleteWithClient(client, url, requestData)
}

func (c *Client) DeleteWithResult(ctx context.Context, path string, body interface{}, result interface{}) (int, error) {
	statusCode, bytes, err := c.Delete(ctx, path, body)
	if err != nil {
		return statusCode, err
	}
	if len(bytes) > 0 || result != nil {
		err = json.Unmarshal(bytes, result)
		if err != nil {
			return statusCode, err
		}
	}
	return statusCode, nil
}

// WithRestyClientFunc 设置获取resty的client的函数
func WithRestyClientFunc(f func() *resty.Client) ClientOption {
	if f == nil {
		f = httputil.GetDefaultClient
	}
	return func(c *Client) {
		c.restyClientFunc = f
	}
}

// WithClientDiscovery 设置服务发现
func WithClientDiscovery(d registry.Discovery) ClientOption {
	return func(o *Client) {
		o.discovery = d
	}
}

// WithClientBalancer 设置负载均衡
func WithClientBalancer(b registry.Balancer) ClientOption {
	return func(o *Client) {
		o.balance = b
	}
}

// WithClientResolver 设置服务解析
func WithClientResolver(r registry.Resolver) ClientOption {
	return func(o *Client) {
		o.resolver = r
	}
}
